<?php

namespace WebmanGrpc\Module;

/**
 * 模块管理器
 * 
 * 负责加载、管理和协调各个模块
 */
class ModuleManager
{
    /**
     * 已注册的模块
     * @var array
     */
    protected static array $modules = [];
    
    /**
     * 已加载的模块
     * @var array
     */
    protected static array $loadedModules = [];
    
    /**
     * 模块依赖图
     * @var array
     */
    protected static array $dependencyGraph = [];
    
    /**
     * 模块加载顺序
     * @var array
     */
    protected static array $loadOrder = [];
    
    /**
     * 注册模块
     * 
     * @param ModuleInterface $module 模块实例
     * @return void
     */
    public static function register(ModuleInterface $module): void
    {
        $name = $module->getName();
        
        if (isset(self::$modules[$name])) {
            throw new \RuntimeException("Module '{$name}' is already registered");
        }
        
        self::$modules[$name] = $module;
        self::$dependencyGraph[$name] = $module->getDependencies();
    }
    
    /**
     * 注销模块
     * 
     * @param string $name 模块名称
     * @return void
     */
    public static function unregister(string $name): void
    {
        if (!isset(self::$modules[$name])) {
            return;
        }
        
        // 如果模块已加载，先停止它
        if (isset(self::$loadedModules[$name])) {
            self::unload($name);
        }
        
        unset(self::$modules[$name]);
        unset(self::$dependencyGraph[$name]);
    }
    
    /**
     * 加载模块
     * 
     * @param string $name 模块名称
     * @param array $config 配置选项
     * @return void
     */
    public static function load(string $name, array $config = []): void
    {
        if (!isset(self::$modules[$name])) {
            throw new \RuntimeException("Module '{$name}' is not registered");
        }
        
        if (isset(self::$loadedModules[$name])) {
            return;  // 模块已加载
        }
        
        // 计算加载顺序
        if (empty(self::$loadOrder)) {
            self::calculateLoadOrder();
        }
        
        // 按顺序加载模块及其依赖
        foreach (self::$loadOrder as $moduleName) {
            if ($moduleName === $name && !isset(self::$loadedModules[$moduleName])) {
                $module = self::$modules[$moduleName];
                $module->initialize($config);
                $module->start();
                self::$loadedModules[$moduleName] = $module;
            }
        }
    }
    
    /**
     * 卸载模块
     * 
     * @param string $name 模块名称
     * @return void
     */
    public static function unload(string $name): void
    {
        if (!isset(self::$loadedModules[$name])) {
            return;  // 模块未加载
        }
        
        // 找到依赖此模块的其他模块
        $dependents = [];
        foreach (self::$dependencyGraph as $module => $dependencies) {
            if (in_array($name, $dependencies)) {
                $dependents[] = $module;
            }
        }
        
        // 先卸载依赖此模块的其他模块
        foreach ($dependents as $dependent) {
            if (isset(self::$loadedModules[$dependent])) {
                self::unload($dependent);
            }
        }
        
        // 停止并卸载模块
        $module = self::$loadedModules[$name];
        $module->stop();
        unset(self::$loadedModules[$name]);
    }
    
    /**
     * 加载所有模块
     * 
     * @param array $configs 配置选项
     * @return void
     */
    public static function loadAll(array $configs = []): void
    {
        // 计算加载顺序
        self::calculateLoadOrder();
        
        // 按顺序加载所有模块
        foreach (self::$loadOrder as $name) {
            if (!isset(self::$loadedModules[$name])) {
                $config = $configs[$name] ?? [];
                self::load($name, $config);
            }
        }
    }
    
    /**
     * 卸载所有模块
     * 
     * @return void
     */
    public static function unloadAll(): void
    {
        // 按相反顺序卸载所有模块
        $reverseOrder = array_reverse(self::$loadOrder);
        
        foreach ($reverseOrder as $name) {
            if (isset(self::$loadedModules[$name])) {
                self::unload($name);
            }
        }
    }
    
    /**
     * 计算模块加载顺序（拓扑排序）
     * 
     * @return void
     */
    protected static function calculateLoadOrder(): void
    {
        $visited = [];
        $temp = [];
        self::$loadOrder = [];
        
        foreach (self::$dependencyGraph as $module => $dependencies) {
            if (!isset($visited[$module])) {
                self::topologicalSort($module, $visited, $temp);
            }
        }
        
        // 反转结果，得到正确的加载顺序
        self::$loadOrder = array_reverse(self::$loadOrder);
    }
    
    /**
     * 拓扑排序辅助函数
     * 
     * @param string $module 模块名称
     * @param array $visited 已访问标记
     * @param array $temp 临时标记
     * @return void
     */
    protected static function topologicalSort(string $module, array &$visited, array &$temp): void
    {
        if (isset($temp[$module])) {
            throw new \RuntimeException("Circular dependency detected involving module '{$module}'");
        }
        
        if (isset($visited[$module])) {
            return;
        }
        
        $temp[$module] = true;
        
        // 访问所有依赖
        if (isset(self::$dependencyGraph[$module])) {
            foreach (self::$dependencyGraph[$module] as $dependency) {
                if (!isset(self::$modules[$dependency])) {
                    throw new \RuntimeException("Dependency '{$dependency}' for module '{$module}' is not registered");
                }
                
                if (!isset($visited[$dependency])) {
                    self::topologicalSort($dependency, $visited, $temp);
                }
            }
        }
        
        unset($temp[$module]);
        $visited[$module] = true;
        self::$loadOrder[] = $module;
    }
    
    /**
     * 获取模块
     * 
     * @param string $name 模块名称
     * @return ModuleInterface|null 模块实例
     */
    public static function getModule(string $name): ?ModuleInterface
    {
        return self::$modules[$name] ?? null;
    }
    
    /**
     * 获取已加载的模块
     * 
     * @param string $name 模块名称
     * @return ModuleInterface|null 模块实例
     */
    public static function getLoadedModule(string $name): ?ModuleInterface
    {
        return self::$loadedModules[$name] ?? null;
    }
    
    /**
     * 获取所有已注册的模块
     * 
     * @return array 模块列表
     */
    public static function getRegisteredModules(): array
    {
        return array_keys(self::$modules);
    }
    
    /**
     * 获取所有已加载的模块
     * 
     * @return array 模块列表
     */
    public static function getLoadedModules(): array
    {
        return array_keys(self::$loadedModules);
    }
    
    /**
     * 获取模块状态
     * 
     * @param string $name 模块名称
     * @return array|null 模块状态信息
     */
    public static function getModuleStatus(string $name): ?array
    {
        $module = self::$loadedModules[$name] ?? null;
        if ($module === null) {
            return null;
        }
        
        return array_merge([
            'name' => $name,
            'version' => $module->getVersion(),
            'loaded' => true,
        ], $module->getStatus());
    }
    
    /**
     * 获取所有模块状态
     * 
     * @return array 所有模块状态信息
     */
    public static function getAllModuleStatus(): array
    {
        $status = [];
        
        foreach (self::$modules as $name => $module) {
            $moduleStatus = [
                'name' => $name,
                'version' => $module->getVersion(),
                'loaded' => isset(self::$loadedModules[$name]),
            ];
            
            if ($moduleStatus['loaded']) {
                $moduleStatus = array_merge($moduleStatus, $module->getStatus());
            }
            
            $status[$name] = $moduleStatus;
        }
        
        return $status;
    }
    
    /**
     * 检查模块是否已注册
     * 
     * @param string $name 模块名称
     * @return bool 是否已注册
     */
    public static function isRegistered(string $name): bool
    {
        return isset(self::$modules[$name]);
    }
    
    /**
     * 检查模块是否已加载
     * 
     * @param string $name 模块名称
     * @return bool 是否已加载
     */
    public static function isLoaded(string $name): bool
    {
        return isset(self::$loadedModules[$name]);
    }
}